<?php
// inc/class/debug.php
namespace smp;

use smp\Base;
use smp\Config;
use smp\Template;

class Debug extends Base {

	// Properties ->
	protected static $logfile;
	protected static $log = array();
	protected static $mode = 'FATAL';
	protected static $protectedArgs = array();
	protected static $errno, $error, $trace, $errorpage;
	protected static $readonly = array('error', 'errno', 'trace', 'readonly', 'secret', 'log', 'mode', 'protectedArgs');
	protected static $secret = array('readonly', 'secret', 'protectedArgs');

	// active ->
	public static function active() {
		if(defined('LogOverwrite')) { return LogOverwrite; }
	return Config::getDebug();
	}

	// backtrace ->
	public static function backtrace($function) { // -!
		if(!is_string($function)) { return NULL; }
		$ret = array_reverse(debug_backtrace());
		foreach($ret as $val) {
			if($val['function'] != $function) { continue; }
			$ret = static::parseTrace($val);
			unset($val);
		break;
		}
		unset($function);
	return $ret;
	}

	// protectArg ->
	public static function protectArg($function, $argument, $class = 'NOCLASS') { // -!
		if(!is_array($argument)) {
			$argument = array($argument);
		}
		if(isset(static::$protectedArgs[$class][$function])) {
			$argument = array_merge(static::$protectedArgs. $argument);
		}
		static::$protectedArgs[$class][$function] = $argument;
	}

	// add ->
	public static function add($error, $type, $errno, $trace = NULL) { // -!
		if(empty(static::$log)) { static::$log = array(); }
		if(!$entry = static::prepare($error, $type, $errno, $trace)) {
			return;
		}
		static::$log[] = $entry;
		unset($error, $errno, $type, $trace, $entry);
	return;
	}

	// flush ->
	public static function flush($raw = false) {
		if(empty(static::$log)) { return; }
		if(Config::getSavelog() and !static::saveToLogfile()) {
			static::add(static::$error, 'FATAL', static::$errno);
		}
		echo static::parseLog($raw);
	}

	// closeLogfile ->
	public static function closeLogfile() {
		if(!is_resource(static::$logfile)) { return; }
		fclose(static::$logfile);
	}

	// prepareLogfile ->
	private static function prepareLogfile() {
		$filename = _base.'logs/errors.'.date('m.d.Y').'.log';
		if(!static::$logfile = fopen($filename, 'a')) {
			static::$errno = 1;
			static::$error = 'Unable to open file ('.$filename.')';
		return false;
		}
	return true;
	}

	// saveToLogfile ->
	private static function saveToLogfile() {
		if(!is_resource(static::$logfile) and !static::prepareLogfile()) {
			return false;;
		}
		$log = strip_tags(static::parseLog(true));
		fwrite(static::$logfile, $log);
	return true;
	}

	// setMode ->
	private static function setMode() {
		if(static::active()) {
			static::$mode = 'DEBUG';
		} else {
			static::$mode = 'FATAL';
		}
	}

	// prepare ->
	private static function prepare($error, $type, $errno, $trace) { // -!
		static::setMode();
		if(!is_int($errno)) { $errno = 0; }
		if(trim($error) == '') {
			$error = 'Empty error message!';
			$type = 'WARN';
			$errno = 0;
		}
		switch ($type) {
			case 'FATAL': break;
			case 'WARN':
				if(static::$mode == 'FATAL') { return NULL; }
				break;
			default;
				if(static::$mode == 'FATAL') { return NULL; }
				$type = 'DEBUG';
		}
		if(is_array($trace) and isset($trace['reset'])) {
			call_user_func($trace['reset']);
			unset($trace['reset']);
		}
		$ret = array(
			'time' => date('G:i:s'),
			'type' => $type,
			'error' => $error,
			'errno' => $errno,
			'trace' => $trace
		);
		unset($error, $type, $errno, $trace);
	return $ret;
	}

	// parseLog ->
	private static function parseLog($raw = false) {

		// Unformated ->
		if($raw) {
			$log = array();
			foreach(static::$log as $val) {
				$temp = $val['time'].' -> '.$val['type'].' -> '.$val['error'].' ('.$val['errno'].')';
				if(isset($val['trace'], $val['trace']['line'], $val['trace']['call'], $val['trace']['file'])) {
					$temp .= ' File: '.$val['trace']['file'].' Line: '.$val['trace']['line'].' Call: '.$val['trace']['call'].' ';
					if(isset($val['trace']['args']) and count($val['trace']['args']) > 0) {
						$arg = array();
						foreach($val['trace']['args'] as $k => $v) {
							$arg[] = $k.': '.$v;
							unset($k, $v);
						}
						$temp .= implode(' ', $arg);
						unset($arg);
					}
				}
				$temp .= '<br />'."\r\n";
				$log[] = $temp;
				unset($temp, $val);
			}
			$ret = implode("\r\n", $log);
			unset($raw, $log);
		return $ret;
		}

		// Formated ->
		$block = 'inc/pages/blocks/';
		$templates = array(
			'main' => Template::prepare($block.'main.htm', true),
			'head' => Template::prepare($block.'block.Head.htm', true),
			'navigation' => Template::prepare('inc/pages/blocks/block.Navigation.htm', true),
			'debug' => array(
				'main' => Template::prepare($block.'cont.Debug.htm', true),
				'entry' => array(
					'notrace' => Template::prepare($block.'debug.Entry.htm', true),
					'trace' => Template::prepare($block.'debug.EntryTraced.htm', true)
				)
			),
			'footer' => Template::prepare('inc/pages/blocks/block.Footer.htm', true)
		);
		$content = array(
			'head' => array(
						'sys.LanguageShort' => 'en', // -!
						'sys.Title' => 'Source MotD - DebugLog',
						'sys.Description' => 'Generated DebugLog',
						'sys.Keywords' => 'smp, debug, log',
						'sys.Author' => 'BrainInBlack'
					)
		);
		$format = array(
			'DEBUG' => 'Debug',
			'WARN' => 'Warning',
			'FATAL' => 'Fatal'
		);
		$entry = array();
		foreach(static::$log as $val) {
			$cont = array(
				'time' => $val['time'],
				'error' => $val['error'],
				'type' => $format[$val['type']],
				'errno' => $val['errno']
			);
			if(isset($val['trace']) and is_array($val['trace'])) {
				$count = count($val['trace']['args']);
				if($count > 0) {
					$num = 1;
					$arg = '';
					foreach($val['trace']['args'] as $k => $v) {
						if($count > $num) {
							$arg .= $k.' = '.$v.', ';
						} else {
							$arg .= $k.' = '.$v;
						}
						$num++;
						unset($k, $v);
					}
				} else {
					$arg = '';
				}
				$cont['trace'] = '<b>'.$val['trace']['call'].'('.$arg.')</b><br />File: '.$val['trace']['file'].' Line: '.$val['trace']['line'];
				$entry[] = Template::process($cont, $templates['debug']['entry']['trace'], true);
				unset($count, $num, $arg);
			} else {
				$entry[] = Template::process($cont, $templates['debug']['entry']['notrace'], true);
			}
			unset($val, $cont);
		}
		$content['debug'] = array(
			'debug.Entrys' => implode("\r\n", $entry),
			'debug.Saved' => (Config::getSavelog() === true ? 'Log was saved!' : 'Log was not saved!')
		);
		$main_cont = array(
			'block.Head' => Template::process($content['head'], $templates['head'], true),
			'block.Navigation' => $templates['navigation'],
			'block.Messages' => '',
			'sys.Content' => Template::process($content['debug'], $templates['debug']['main'], true),
			'block.Footer' => $templates['footer']
		);
	return Template::process($main_cont, $templates['main'], true);
	}

	// protectedArg ->
	private static function protectedArg($arg, $function, $class = 'NOCLASS') {
		$ret = false;
		if(isset(static::$protectedArgs[$class][$function])) {
			foreach(static::$protectedArgs[$class][$function] as $val) {
				if($val == $arg) {
					$ret = true;
					break;
				}
				unset($val);
			}
		}
		unset($arg, $function, $class);
	return $ret;
	}

	// parseTrace ->
	private static function parseTrace($trace) {
		if(!is_array($trace)) {
			die('FatalError: Can not parse non array trace!');
		}
		if(isset($trace['object'])) {
			unset($trace['object']);
		}
		if(is_array($trace['args']) and count($trace['args']) > 0) {
			foreach($trace['args'] as $key => $val) {
				unset($trace['args'][$key]);
				$key++;
				if(!isset($trace['class'])) {
					$trace['class'] = 'NOCLASS';
				}
				if(static::protectedArg($key, $trace['function'], $trace['class'])) {
					$trace['args']['Arg'.$key] = '***PROTECTED***';
					unset($key, $val);
					continue;
				} else {
					// InsertArgOverwritesHere ->
				}
				$trace['args']['Arg'.$key] = $val;
				unset($key, $val);
			}
		}
		if(isset($trace['class'])) {
			if($trace['function'] == '__construct') { $trace['function'] = 'construct'; }
			$trace['call'] = $trace['class'].$trace['type'].$trace['function'];
			$trace['reset'] = $trace['class'].'::resetTrace';
			unset($trace['class'], $trace['type'], $trace['function']);
		} else {
			$trace['call'] = $trace['function'];
			unset($trace['function']);
		}
	return $trace;
	}

}

?>